package hr.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import hr.entity.*;
import hr.entity.vo.*;
import hr.entity.vo.request.DepartureInfoVO;
import hr.entity.vo.response.EmployeeVo;
import hr.entity.vo.response.EntryStatusVo;
import hr.mapper.*;
import hr.service.EmployeesService;
import hr.utils.CommonUtils;
import hr.utils.Uuid.Snowflake;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.*;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * <p>
 * 员工信息表 服务实现类
 * </p>
 *
 * @author zzh
 * @since 2023-12-04
 */
@Service
public class EmployeesServiceImpl extends ServiceImpl<EmployeesMapper, Employee> implements EmployeesService {

    @Autowired
    private EmployeesMapper employeesMapper;
    @Value("${nginx.path.form}")
    private String FORM_PATH;
    @Value("${nginx.path.formTemplate}")
    private String FORM_TEMPLATE;
    @Value("${nginx.path.departFormTemplate}")
    private String DEPART_FORM_TEMPLATE;
    @Autowired
    private Snowflake snowflake;
    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private EmployeeOnboardingLinkMapper employeeOnboardingLinkMapper;

    @Autowired
    private EmployeeOnboardingFormMapper employeeOnboardingFormMapper;
    @Autowired
    private SysMapper sysMapper;
    @Autowired
    private DepartmentMapper departmentMapper;
    @Autowired
    private PositionMapper positionMapper;
    @Autowired
    private EmployeemovementMapper employeemovementMapper;
    @Autowired
    private EmployeeDepartFormMapper employeeDepartFormMapper;

    @Override
    public Employee login(LoginVo vo) throws Exception {
        String password = vo.getPassword();
        String username = vo.getUsername();
        if (CommonUtils.checkParams(username, password)) {
            throw new Exception("输入用户名或密码为空");
        }

        // 游客登录
        if (username.equals("yk") && password.equals("yk")) {
            Employee employee = new Employee();
            // 设置临时游客id 随机数
            employee.setEmployeeId(new Random().nextLong());
            employee.setName("yk");

            // 入职报道链接被激活
            QueryWrapper<EmployeeOnboardingLink> wrapper = Wrappers.query();
            wrapper.eq("url_snow_id", vo.getUrlSnowId());
            EmployeeOnboardingLink onboardingReport = employeeOnboardingLinkMapper.selectOne(wrapper);
            if (onboardingReport == null) {
                throw new Exception("系统未知错误，无法查询到该表单是否真实存在！");
            }
            // 更改表单状态 已激活 1
            onboardingReport.setUrlState((byte) 1);
            int update = employeeOnboardingLinkMapper.updateById(onboardingReport);
            if (update == 0) {
                throw new Exception("链接状态更新失败");
            }
            return employee;
        }
        Employee emp = employeesMapper.selectOneByUsernamePassword(username, password);

        if (CommonUtils.checkParamsObj(emp)) {
            throw new Exception("用户名或密码错误");
        }

        return emp;
    }

    @Override
    public List<EntryStatusVo> findStateFillLink(Paging page) {
        List<EntryStatusVo> list = employeeOnboardingLinkMapper.selectLinkState(page.getCurrentPage() - 1, page.getPageSize());
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        list.forEach(e -> {
            // 格式化时间
            e.setCreateTimeStr(LocalDateTime.ofInstant(Instant.ofEpochMilli(e.getCreateTime()), ZoneId.systemDefault()).format(formatter));
        });
        long count = employeeOnboardingLinkMapper.selectCount(null);
        page.setTotalEntries(count);
        page.setTotalPage(count % page.getPageSize() == 0 ? count / page.getPageSize() : count / page.getPageSize() + 1);
        return list;
    }

    @Override
    public List<?> selectAllDepartments(ObtainDepartment obtainDepartment) {
        obtainDepartment.setCurrentPage(obtainDepartment.getCurrentPage() - 1);
        // 查询参数可能是姓名和电话号码
        String name = null;
        String phone = null;
        if (obtainDepartment.getQueryCriteria().isBlank()) {
            obtainDepartment.setQueryCriteria(null);
        } else if (obtainDepartment.getQueryCriteria().contains(",") || obtainDepartment.getQueryCriteria().contains("，")) {
            String[] split = obtainDepartment.getQueryCriteria().split("[,，]");
            name = split[0];
            phone = split[1];
        } else {
            name = obtainDepartment.getQueryCriteria();
            phone = obtainDepartment.getQueryCriteria();
        }

        List<EmployeeVo> list = employeesMapper.selectAllByPage(obtainDepartment, name, phone);
        list.forEach(e -> {
            e.setHireDate(CommonUtils.convertTimestampToYMDAndTime(e.getJoinDate()));
        });

        // 设置分页信息
        long count = employeesMapper.selectThisQueryPageCount(obtainDepartment, name, phone);
        obtainDepartment.setTotalEntries(count);
        obtainDepartment.setTotalPage(count % obtainDepartment.getPageSize() == 0 ?
                count / obtainDepartment.getPageSize() :
                count / obtainDepartment.getPageSize() + 1);
        return list;
    }

    @Override
    public int brokenLink(EmployeeOnboardingLink vo) throws Exception {
        vo = employeeOnboardingLinkMapper.selectById(vo.getId());
        if (vo == null) {
            throw new Exception("没有相关信息");
        }
        vo.setUrlState((byte) 3);
        return employeeOnboardingLinkMapper.updateById(vo);
    }

    @Override
    public boolean collectOnboardingReportsAndReports(FormInline form) throws Exception {
        // 更新填写状态 TODO 考虑给数据上锁 因为这个提交可能被重复且并发调用
        // TODO 处理静态页面 发送该请求也需要带上url_snow_id
        // ConnectionOnboardingReports cor = new ConnectionOnboardingReports();
        // cor.setUrlSnowId(form.getSnowId());
        // 查看表单是否已经失效
        QueryWrapper<EmployeeOnboardingLink> wrap = new QueryWrapper<>();
        wrap.nested(i -> i.eq("url_state", 3).or().eq("url_state", 2)).and(i -> i.eq("url_snow_id", form.getSnowId()));
        // wrap.eq("url_state", 3).or().eq("url_state", 2).eq("url_snow_id", form.getSnowId());
        EmployeeOnboardingLink one = employeeOnboardingLinkMapper.selectOne(wrap);
        if (one != null) {
            // 查询到说明该链接失效了，拒绝提交
            throw new Exception(String.format("该链接%s已失效或已被填写，无法提交到数据库", one.getFormUrl()));
        }
        // 设置表单状态为已提交状态 状态标识为2
        UpdateWrapper<EmployeeOnboardingLink> wrapper = new UpdateWrapper<>();
        wrapper.eq("url_snow_id", form.getSnowId());
        wrapper.set("url_state", 2);
        int update = employeeOnboardingLinkMapper.update(null, wrapper);
        if (update == 0) {
            throw new Exception("该表单可能已被提交过，禁止重复提交");
        }
        // 添加数据
        int insert = employeeOnboardingFormMapper.insert(formInlineToFromWriteInfo(form));

        return insert != 0;
    }

    private EmployeeOnboardingForm formInlineToFromWriteInfo(FormInline form) {
        EmployeeOnboardingForm employeeOnboardingForm = new EmployeeOnboardingForm();
        employeeOnboardingForm.setAge(form.getAge());
        employeeOnboardingForm.setUrlSnowId(form.getSnowId());
        employeeOnboardingForm.setName(form.getName());
        // 性别查库 TODO
        employeeOnboardingForm.setSex(form.getSex());
        employeeOnboardingForm.setBirthday(CommonUtils.convertYMDDateToTimestamp(form.getBirth()));
        // 婚姻状态查库
        employeeOnboardingForm.setMaritalStatus(form.getMarriage());
        // 民族 查民族表 TODO
        employeeOnboardingForm.setNation(form.getNation());
        // 地址 TODO 使用ip获取他的真实地址
        employeeOnboardingForm.setAddress("湖北十堰");
        // 身份证
        employeeOnboardingForm.setIdentityCard(form.getUserId());
        employeeOnboardingForm.setPhoneNumber(form.getPhoneNumber());
        employeeOnboardingForm.setDepartmentId(form.getDepId());
        employeeOnboardingForm.setPositionId(form.getPartId());
        employeeOnboardingForm.setEmail(form.getEmail());

        return employeeOnboardingForm;
    }

    @Override
    public FormInline viewOnboardingForm(String urlId) throws Exception {
        /*
        1、根据urlId去connection_onboarding_reports查到snowUrlId
        2、链接查询from_write_info表的url_snow_id
         */
        EmployeeOnboardingForm info = employeeOnboardingFormMapper.selectOneByWriteUrlId(urlId);
        if (info == null) {
            throw new Exception("不能查询到相关信息");
        }

        return this.formWriteInfoToFormInline(info);
    }


    @Override
    public FormInline viewEmployeeDetails(String empId) throws Exception {
        if (empId.isBlank()) {
            throw new Exception("请求的员工id不是合法的参数值");
        }
        EmployeeOnboardingForm employeeOnboardingForm = employeeOnboardingFormMapper.selectOneByEmpId(empId);
        if (employeeOnboardingForm == null) {
            throw new Exception("没有查询到相关记录");
        }
        return this.formWriteInfoToFormInline(employeeOnboardingForm);
    }

    @Override
    public boolean updateEmployeeDetails(FormInline form) {
        // TODO 员工详细信息修改逻辑 2023年12月17日21:01:51
        // TODO 考虑事务
        EmployeeOnboardingForm oneById = employeeOnboardingFormMapper.selectById(form.getId());
        EmployeeOnboardingForm employeeOnboardingForm = formInlineToFromWriteInfo(form);
        employeeOnboardingForm.setId(form.getId());
        int rows = employeeOnboardingFormMapper.updateById(employeeOnboardingForm);

        // 监测岗位是否发生了变化
        if (form.getPartId().longValue() == oneById.getDepartmentId().longValue()) {
            return rows != 0;
        }
        // 添加岗位调动记录
        EmployeeMovement employeeMovement = new EmployeeMovement();
        // 查员工
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        wrapper.eq("onboarding_form_id", form.getId());
        Employee employee = employeesMapper.selectOne(wrapper);
        employeeMovement.setEmployeeId(employee.getEmployeeId());
        employeeMovement.setMovementDate(System.currentTimeMillis());
        employeeMovement.setFromDepartmentId(oneById.getDepartmentId());
        employeeMovement.setToDepartmentId(form.getDepId());
        employeeMovement.setFromPositionId(oneById.getPositionId());
        employeeMovement.setToPositionId(form.getPartId());
        int insert = employeemovementMapper.insert(employeeMovement);
        return insert * rows != 0;
    }

    @Override
    public Integer getCount(PositionEmployeesVo positionEmployeesVO) {
        Integer count = employeesMapper.getCount(positionEmployeesVO.getName());
        return count;
    }

    /**
     * 离职申请
     *
     * @return
     */
    @Override
    public String resignationApplication() throws IOException {
        // 生成唯一id id做文件名
        long id = snowflake.nextId();
        // 文件操作 生成html
        File file = new File(FORM_PATH + DEPART_FORM_TEMPLATE);
        File newfile = new File(FORM_PATH + id + DEPART_FORM_TEMPLATE.substring(DEPART_FORM_TEMPLATE.lastIndexOf(".")));
        BufferedReader br = new BufferedReader(new FileReader(file));
        BufferedWriter bw = new BufferedWriter(new FileWriter(newfile));
        String readLineStr;
        while ((readLineStr = br.readLine()) != null) {
            bw.write(readLineStr);
            bw.newLine();
        }
        bw.close();
        br.close();

        return "liujijiself.f3.ttkt.cc:80/form/" + id + DEPART_FORM_TEMPLATE.substring(DEPART_FORM_TEMPLATE.lastIndexOf("."));
    }

    @Override
    public boolean submitResignationApplicationForm(DepartureInfoVO vo) {
        int insert = employeeDepartFormMapper.insertOne(vo, CommonUtils.convertYMDDateToTimestamp(vo.getDepartureDate()));
        return insert != 0;
    }

    @Override
    public IPage<?> getResignationApplicationForm(ObtainDepartment paging) {
        paging.setCurrentPage(paging.getCurrentPage() - 1);
        List<EmployeeDepartForm> list = employeeDepartFormMapper.selectByPage(paging);
        long count = employeeDepartFormMapper.selectByPageCount(paging);
        IPage<EmployeeDepartForm> iPage = new Page<>(paging.getCurrentPage(), count);
        iPage.setRecords(list);
        return iPage;
    }

    @Override
    public boolean agreeDepart(EmployeeDepartForm vo) {
        EmployeeDepartForm employeeDepartForm = new EmployeeDepartForm();
        employeeDepartForm.setId(vo.getId());
        employeeDepartForm.setDepartMent(2L);
        // 更新
        int update = employeeDepartFormMapper.updateById(employeeDepartForm);
        // 删除员工
        int delete = employeesMapper.deleteById(vo.getEmployeeId());
        return update * delete != 0;
    }

    @Override
    public Map<String, Object> getEmployeeRatio() {
        HashMap<String, Object> map = new HashMap<>();
        ArrayList<Object> arrayList = new ArrayList<>();
        arrayList.add(employeesMapper.selectEmployeeMaritalStatusRatio());
        arrayList.add(employeesMapper.selectEmployeeSexRatio());
        map.put("list", arrayList);
        return map;
    }

    @Override
    public Map<String, Object> HandleSevenDayInAndLeave() {
        int dayLength = 7;
        String[] days = new String[dayLength];
        long[] daysTime = new long[dayLength];
        days[0] = LocalDate.now().toString();
        // 向前推6天
        for (int i = 1; i < dayLength; i++) {
            days[i] = LocalDate.now().minusDays(i).toString();
        }
        for (int i = 0; i < dayLength; i++) {
            daysTime[i] = CommonUtils.convertYMDDateToTimestamp(days[i]);
        }

        // 获取六天前的时间戳开始时间
        long timeLong = CommonUtils.convertYMDDateToTimestamp(days[days.length - 1]);

        List<Map<String, String>> list = employeesMapper.selectSevenDaysInAndOut(timeLong);

        long[] inList = new long[dayLength];
        long[] outList = new long[dayLength];
        list.forEach(e -> {
            // 分析时间落入区间
            int index = 0;
            for (long time : daysTime) {
                if (time < Long.parseLong(String.valueOf(e.get("time")))) {
                    break;
                }
                ++index;
            }
            if (e.get("flag").equals("pepIn")) {
                inList[index]++;
            } else if (e.get("flag").equals("pepOut")) {
                outList[index]++;
            }
        });
        HashMap<String, Object> map = new HashMap<>();
        map.put("dayList", reversalArrToList(days));
        map.put("inList", reversalArrToList(inList));
        map.put("outList", reversalArrToList(outList));
        return map;
    }

    private List<Object> reversalArrToList(Object[] arr) {
        List<Object> list = Arrays.asList(arr);
        Collections.reverse(list);
        return list;
    }

    private List<Object> reversalArrToList(long[] arr) {
        List<Object> list = new ArrayList<>();
        for (int i = arr.length - 1; i >= 0; i--) {
            list.add(arr[i]);
        }
        return list;
    }


    private FormInline formWriteInfoToFormInline(EmployeeOnboardingForm info) {
        FormInline formInline = new FormInline();

        formInline.setId(info.getId());
        formInline.setAge(info.getAge());
        formInline.setName(info.getName());
        formInline.setSex(info.getSex());
        formInline.setSexStr(sysMapper.selectGenderStr(info.getSex()));
        formInline.setBirth(CommonUtils.convertTimestampToYMD(info.getBirthday()));
        formInline.setEmail(info.getEmail());
        formInline.setMarriage(info.getMaritalStatus());
        formInline.setMarriageStr(sysMapper.selectMarriageStr(info.getMaritalStatus()));
        formInline.setNation(info.getNation());
        formInline.setNationStr(sysMapper.selectNationStr(info.getNation()));
        formInline.setUserId(info.getIdentityCard());
        formInline.setPhoneNumber(info.getPhoneNumber());
        formInline.setDepId(info.getDepartmentId());
        formInline.setDepStr(departmentMapper.selectById(info.getDepartmentId()).getDepartmentName());
        formInline.setPartId(info.getPositionId());
        formInline.setPartStr(positionMapper.selectById(info.getPositionId()).getPositionName());

        return formInline;
    }

    @Override
    public boolean handleFormSubmission(String formId, String handleMethod) throws Exception {
        EmployeeOnboardingForm oneByID = employeeOnboardingFormMapper.selectById(formId);
        if (oneByID == null) {
            throw new Exception("不存在该表单记录");
        }
        if (!(handleMethod.equals("reject") || handleMethod.equals("agree"))) {
            throw new Exception("处理方式 handleMethod 只能是 reject 或者是 agree");
        }
        if (handleMethod.equals("agree")) {
            // 将该人添加到员工表
            Employee employee = new Employee();
            // 设置基准薪资
            employee.setSalary(3000d);
            // 员工状态 状态3：试用考核
            employee.setEmployeeState(3);
            // 状态2：试用员工
            employee.setEmployeeType(2);
            employee.setIsDeleted(0);
            employee.setOnboardingFormId(oneByID.getId());
            employee.setJoinDate(System.currentTimeMillis());
            int insert = employeesMapper.insert(employee);

            // 向岗位调动添加数据
            EmployeeMovement employeeMovement = new EmployeeMovement();
            employeeMovement.setEmployeeId(employee.getEmployeeId());
            employeeMovement.setMovementDate(System.currentTimeMillis());
            employeeMovement.setToDepartmentId(oneByID.getDepartmentId());
            employeeMovement.setToPositionId(oneByID.getPositionId());
            employeemovementMapper.insert(employeeMovement);
            return insert != 0;
        }
        return true;
    }

    @Override
    public List<Employee> searchByEmployeeName(String employeeName) {
        // List<Employees > list= employeesMapper.searchByEmployeeName(employeeName);
        return null;
    }

    @Override
    public Map<String, String> fillInOnboardingRegistrationInformation(Long loginId) throws Exception {
        // 生成唯一id id做文件名
        long id = snowflake.nextId();
        // 文件操作 生成html
        File file = new File(FORM_PATH + FORM_TEMPLATE);
        File newfile = new File(FORM_PATH + id + FORM_TEMPLATE.substring(FORM_TEMPLATE.lastIndexOf(".")));
        BufferedReader br = new BufferedReader(new FileReader(file));
        BufferedWriter bw = new BufferedWriter(new FileWriter(newfile));
        String readLineStr;
        while ((readLineStr = br.readLine()) != null) {
            bw.write(readLineStr);
            bw.newLine();
            // 给这个链接html文件塞入id
            if (readLineStr.contains("username: \"yk\",")) {
                bw.write("urlSnowId: '" + id + "',");
                bw.newLine();
            }
        }
        bw.close();
        br.close();

        HashMap<String, String> map = new HashMap<>();
        map.put("formId", String.valueOf(id));
        // 生成该html的链接
        String formLink = "liujijiself.f3.ttkt.cc:80/form/" + id + FORM_TEMPLATE.substring(FORM_TEMPLATE.lastIndexOf("."));
        map.put("formLink", formLink);

        long nowCtm = System.currentTimeMillis();
        // 一小时后过期
        long delCtm = nowCtm + 60 * 60 * 1000;

        // 将这条链接记录存入到数据库
        EmployeeOnboardingLink onboardingReport = new EmployeeOnboardingLink(null, formLink, nowCtm, delCtm, loginId, null, newfile.toString(), newfile.getName(), id, 0, (byte) 0);
        int insert = employeeOnboardingLinkMapper.insert(onboardingReport);
        if (insert == 0) {
            throw new Exception("生成链接无法存入到数据库！");
        }

        // 文件要定时删除 借助redis zset结构 分数是截止时间戳 值是id 凡是过期的时间戳，被Spring定时任务扫描并删除
        Boolean add = redisTemplate.opsForZSet().add("fillInOnboardingRegistrationInformation", newfile.toString(), delCtm);

        if (Boolean.FALSE.equals(add)) {
            throw new Exception("添加定时任务失败：无法将入职报到信息填写文件添加到文件删除任务，文件位置 " + newfile);
        }
        return map;
    }

}
